package tcp_servicer

import (
	"bufio"
	"fmt"
	"ifms/db_servicer"
	"net"
	"regexp"
	"time"
)

type DevMsg struct {
	dataType string
	dataStr  string
}

type Device struct {
	conn     net.Conn
	incoming chan string
	outgoing chan string
	db       chan DevMsg // string // for all data will send to DB
	reader   *bufio.Reader
	writer   *bufio.Writer
	quiting  chan string
	quited   chan bool
	// quitAnnounce chan bool
	device_id string
	ip        string
	port      string
	tcp_svr   *Tcp_Server
}

func CreateDevice(conn net.Conn, svr *Tcp_Server) (*Device, string) {
	remoteHost := conn.RemoteAddr().String()
	ip, port, err := net.SplitHostPort(remoteHost)

	if err != nil {
		logger.Println(err)
	}
	if !regexp.MustCompile(`^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$`).MatchString(ip) {
		ip = "localhost"
	}

	var msg []string
	var message, device_id string

	if line, err := bufio.NewReader(conn).ReadString('\n'); err == nil {
		message = string(line)
		msg = regexp.MustCompile(";").Split(message, -1)
		logger.Printf("Creating device... Initial Msg from device: [%v]", msg)

	} else {
		logger.Printf("Reading Error:%s\n", err)
		return nil, "nil"
	}

	switch msg[0] {

	case "#":
		device_id = msg[1]
	case "@":
		device_id = "HTTP"
	default:
		device_id = "NA"
		logger.Printf("Wrong data format:%v", message)
	}

	reader := bufio.NewReader(conn)
	writer := bufio.NewWriter(conn)

	device := &Device{
		conn:      conn,
		incoming:  make(chan string),
		outgoing:  make(chan string),
		db:        make(chan DevMsg),
		quiting:   make(chan string),
		quited:    make(chan bool),
		reader:    reader,
		writer:    writer,
		tcp_svr:   svr,
		ip:        ip,
		port:      port,
		device_id: device_id,
	}
	device.Listen()

	logger.Printf("Device [%v] created and listen started.\n", device_id)

	return device, message

}

func (self *Device) SetDevice_id(id string) {
	self.device_id = id
}

func (self *Device) GetDevice_ID() string {
	return self.device_id
}

func (self *Device) Listen() {
	go self.Read()
	// go self.Write()
	go self.ProcessDB()
}

func (self *Device) ProcessDB() {
	for {

		dataMsg := <-self.db

		incoming_msg := db_servicer.Incomeing_msg{self.ip, self.port, self.device_id, dataMsg.dataType, dataMsg.dataStr, nil}
		retCode, retMsg := db_servicer.Save_incoming_msg(incoming_msg)

		if dataMsg.dataType != "6" { // we do not log heart beat for DB process

			logger.Printf("->DB: [%s];%v;%v\n", self.device_id, dataMsg.dataType, dataMsg.dataStr)
		}
		if retCode == 0 {
			self.processOutGoingMsg(retMsg)
		}

	}
}

func (self *Device) processOutGoingMsg(message string) (int8, string) {
	var logids string
	var retFlag string

	outMsgs := regexp.MustCompile(" : ").Split(message, -1)

	for msg := range outMsgs {

		s := regexp.MustCompile("~").Split(outMsgs[msg], -1)

		//		if _, err := conn.Write([]byte(s[1] + "\n")); err != nil {
		retFlag = self.Write(s[1] + "\n")

		if regexp.MustCompile(";[0-9A];").FindString(s[1]) != ";1;" { //we ingore time sync in log
			logger.Println("Send to Device:", s[1])
		}

		logids += fmt.Sprintf("%s,%s;", s[0], retFlag)

		time.Sleep(120 * time.Millisecond)

	}

	return db_servicer.Outgoing_msg_done(logids)

}

func (self *Device) Read() {
L:
	for {
		select {
		case <-self.quited:
			break L

		default:
			if line, _, err := self.reader.ReadLine(); err == nil {
				self.incoming <- string(line)
			} else {
				logger.Printf("Device[%v] Read error: %s\n", self.device_id, err)
				self.quit()

				break L
			}

		}

	}
	logger.Printf("Device[%v] Read exited...\n", self.device_id)

}

func (self *Device) Write(data string) string {
	// we may never have data less than 5 byte
	if len(data) < 5 {
		return "F"
	}

	if _, err := self.writer.WriteString(data + "\n"); err != nil {
		logger.Printf("Device[%v] Write error:%s\n", self.device_id, err)
		self.quit()
		return "F"
	}
	if err := self.writer.Flush(); err != nil {
		logger.Printf("Device[%v] Flush error: %s\n", self.device_id, err)
		self.quit()
		return "F"
	}

	return "S"

}

/*
func (self *Device) Write() {
	for data := range self.outgoing {
		if _, err := self.writer.WriteString(data + "\n"); err != nil {
			logger.Printf("Device[%v] Write error:%s\n", self.device_id, err)
			self.quit()
			break

		}
		if err := self.writer.Flush(); err != nil {
			logger.Printf("Device[%v] Flush error: %s\n", self.device_id, err)
			self.quit()
			break
		}

	}
}

*/

//Tell server that we are quiting....
//Please refer to func (self *Tcp_Server) join(conn net.Comm) in
// ifms/tcp_servicer/tcp_server.go

func (self *Device) quit() {

	self.quiting <- self.device_id
	close(self.quited)

}

//cleaning up device here, this will called after tell the server
// or, get quit announced from server side

func (self *Device) close() {

	logger.Printf(" Clearning device [%v].... \n", self.device_id)

	// close(self.outgoing)
	//close(self.incoming)

	if self.conn != nil {

		self.conn.Close()
		logger.Printf(" Closing connection: %v\n", self.conn)

	}
	logger.Printf(" Device [%v] cleared.... \n ", self.device_id)
}
